<template>
  <div class="bgView" :style="style">
    <div :class="['json-view', length ? 'closeable' : '']" :style="'font-size:' + fontSize + 'px'">
      <span @click="toggleClose" :class="['angle', innerclosed ? 'closed' : '']" v-if="length"></span>
      <div class="content-wrap">
        <p class="first-line">
          <span v-if="jsonKey" class="json-key">"{{ jsonKey }}":</span>
          <span v-if="length">
            {{ prefix }}
            {{ innerclosed ? '...' + subfix : '' }}
            <span class="json-note">{{ innerclosed ? ' // count: ' + length : '' }}</span>
          </span>
          <span v-if="!length">{{ isArray ? '[]' : '{}' }}</span>
        </p>
        <div v-if="!innerclosed && length" class="json-body">
          <template v-for="(item, index) in items">
            <json-view :closed="closed" v-if="item.isJSON" :json="item.value" :jsonKey="item.key" :isLast="index === items.length - 1"></json-view>
            <p class="json-item" v-else>
              <span class="json-key">{{ isArray ? '' : '"' + item.key + '"' }}</span>
              :
              <span class="json-value">{{ item.value + (index === items.length - 1 ? '' : ',') }}</span>
            </p>
          </template>
          <span v-show="!innerclosed" class="body-line"></span>
        </div>
        <p v-if="!innerclosed && length" class="last-line">
          <span>{{ subfix }}</span>
        </p>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'jsonView',
  props: {
    json: [Object, Array],
    jsonKey: {
      type: String,
      default: ''
    },
    closed: {
      type: Boolean,
      default: false
    },
    isLast: {
      type: Boolean,
      default: true
    },
    fontSize: {
      type: Number,
      default: 14
    },
    maxHeight: {
      type: Number,
      default: 0
    }
  },
  created() {
    this.innerclosed = this.closed;
    this.$watch('closed', () => {
      this.innerclosed = this.closed;
    });
  },
  data() {
    return {
      innerclosed: true
    };
  },
  methods: {
    isObjectOrArray(source) {
      const type = Object.prototype.toString.call(source);
      const res = type === '[object Array]' || type === '[object Object]';
      return res;
    },
    toggleClose() {
      if (this.innerclosed) {
        this.innerclosed = false;
      } else {
        this.innerclosed = true;
      }
    }
  },
  computed: {
    style() {
      return {
        'min-height': this.maxHeight ? this.maxHeight + 'px' : 'auto',
        'max-height': this.maxHeight ? this.maxHeight + 'px' : 'auto',
        overflow: 'auto'
      };
    },
    isArray() {
      return Object.prototype.toString.call(this.json) === '[object Array]';
    },
    length() {
      return this.isArray ? this.json.length : Object.keys(this.json).length;
    },
    subfix() {
      return (this.isArray ? ']' : '}') + (this.isLast ? '' : ',');
    },
    prefix() {
      return this.isArray ? '[' : '{';
    },
    items() {
      if (this.isArray) {
        return this.json.map(item => {
          const isJSON = this.isObjectOrArray(item);
          return {
            value: isJSON ? item : JSON.stringify(item),
            isJSON,
            key: ''
          };
        });
      }
      const json = this.json;
      return Object.keys(json).map(key => {
        const item = json[key];
        const isJSON = this.isObjectOrArray(item);
        return {
          value: isJSON ? item : JSON.stringify(item),
          isJSON,
          key
        };
      });
    }
  }
};
</script>

<style lang="less" scoped>
.bgView {
  padding: 0;
  height: 100%;
  background-color: transparent;

  .json-view {
    position: relative;
    display: block;
    width: 100%;
    height: 100%;
    white-space: nowrap;
    padding-left: 14px;
    box-sizing: border-box;
    .json-note {
      color: #909399;
    }
    .json-key {
      color: rgb(147, 98, 15);
    }
    .json-value {
      color: rgb(24, 186, 24);
      word-break: break-all;
      white-space: normal;
    }
    .json-item {
      margin: 0;
      padding-left: 14px;
      line-height: 1.5em;
    }
    .first-line {
      padding: 0;
      margin: 0;
    }
    .json-body {
      position: relative;
      padding: 0;
      margin: 0;

      .body-line {
        position: absolute;
        height: 100%;
        width: 0;
        border-left: dashed 1px #bbb;
        top: 0;
        left: 2px;
      }
    }
    .last-line {
      padding: 0;
      margin: 0;
    }
    .angle {
      position: absolute;
      display: block;
      cursor: pointer;
      float: left;
      width: 14px;
      text-align: center;
      left: 0;

      &::after {
        content: '';
        display: inline-block;
        width: 0;
        height: 0;
        vertical-align: middle;
        border-top: solid 6px #333;
        border-left: solid 4px transparent;
        border-right: solid 4px transparent;
      }
      &.closed::after {
        border-left: solid 6px #333;
        border-top: solid 4px transparent;
        border-bottom: solid 4px transparent;
      }
    }
  }
}
</style>
